x86/HVM: sanity check xsave area when migrating or restoring from older Xen versions
authorDon Koch <dkoch@verizon.com>
Mon, 27 Oct 2014 15:45:09 +0000 (16:45 +0100)
committerJan Beulich <jbeulich@suse.com>
Mon, 27 Oct 2014 15:45:09 +0000 (16:45 +0100)
Xen 4.3.0, 4.2.3 and older transferred a maximum sized xsave area (as
if all the available XCR0 bits were set); the new version only
transfers based on the actual XCR0 bits. This may result in a smaller
area if the last sections were missing (e.g., the LWP area from an AMD
machine). If the size doesn't match the XCR0 derived size, the size is
checked against the maximum size and the part of the xsave area
between the actual and maximum used size is checked for zero data. If
either the max size check or any part of the overflow area is
non-zero, we return with an error.

Signed-off-by: Don Koch <dkoch@verizon.com>
Reviewed-by: Jan Beulich <jbeulich@suse.com>
Reviewed-by: Andrew Cooper <andrew.cooper3@citrix.com>
xen/arch/x86/hvm/hvm.c

index 559b7690220ea46df3650df415ac20be9f5be9cb..78f519dc8d76df557644b522a3346837e1429537 100644 (file)
@@ -1971,6 +1971,7 @@ static int hvm_load_cpu_xsave_states(struct domain *d, hvm_domain_context_t *h)
     struct vcpu *v;
     struct hvm_hw_cpu_xsave *ctxt;
     struct hvm_save_descriptor *desc;
+    unsigned int i, desc_start;
 
     /* Which vcpu is this? */
     vcpuid = hvm_load_instance(h);
@@ -2011,15 +2012,8 @@ static int hvm_load_cpu_xsave_states(struct domain *d, hvm_domain_context_t *h)
                         save_area) + XSTATE_AREA_MIN_SIZE);
         return -EINVAL;
     }
-    size = HVM_CPU_XSAVE_SIZE(xfeature_mask);
-    if ( desc->length > size )
-    {
-        printk(XENLOG_G_WARNING
-               "HVM%d.%d restore mismatch: xsave length %u > %u\n",
-               d->domain_id, vcpuid, desc->length, size);
-        return -EOPNOTSUPP;
-    }
     h->cur += sizeof (*desc);
+    desc_start = h->cur;
 
     ctxt = (struct hvm_hw_cpu_xsave *)&h->data[h->cur];
     h->cur += desc->length;
@@ -2038,10 +2032,24 @@ static int hvm_load_cpu_xsave_states(struct domain *d, hvm_domain_context_t *h)
     size = HVM_CPU_XSAVE_SIZE(ctxt->xcr0_accum);
     if ( desc->length > size )
     {
+        /*
+         * Xen 4.3.0, 4.2.3 and older used to send longer-than-needed
+         * xsave regions.  Permit loading the record if the extra data
+         * is all zero.
+         */
+        for ( i = size; i < desc->length; i++ )
+        {
+            if ( h->data[desc_start + i] )
+            {
+                printk(XENLOG_G_WARNING
+                       "HVM%d.%u restore mismatch: xsave length %#x > %#x (non-zero data at %#x)\n",
+                       d->domain_id, vcpuid, desc->length, size, i);
+                return -EOPNOTSUPP;
+            }
+        }
         printk(XENLOG_G_WARNING
-               "HVM%d.%d restore mismatch: xsave length %u > %u\n",
+               "HVM%d.%u restore mismatch: xsave length %#x > %#x\n",
                d->domain_id, vcpuid, desc->length, size);
-        return -EOPNOTSUPP;
     }
     /* Checking finished */
 
@@ -2050,7 +2058,8 @@ static int hvm_load_cpu_xsave_states(struct domain *d, hvm_domain_context_t *h)
     if ( ctxt->xcr0_accum & XSTATE_NONLAZY )
         v->arch.nonlazy_xstate_used = 1;
     memcpy(v->arch.xsave_area, &ctxt->save_area,
-           desc->length - offsetof(struct hvm_hw_cpu_xsave, save_area));
+           min(desc->length, size) - offsetof(struct hvm_hw_cpu_xsave,
+           save_area));
 
     return 0;
 }